package org.lmj.servlet;

import org.lmj.annotation.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DimDispacherServlet extends HttpServlet {
    //所有配置信息存入了Properties种
    private Properties p=new Properties();

    private List<String> classNames=new ArrayList<>();

    private Map<String,Object> ioc=new HashMap<>();

    //private Map<String, Method>handlerMapping=new HashMap<String, Method>();
    private List<Handler> handlerMapping=new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {



        // 1. 加载配置文件
        //config.getInitParameter("contextConfigLocation")
        // 对应着
        // <init-param>
        //            <param-name>contextConfigLocation</param-name>
        //            <param-value>classpath:application.properties</param-value>
        //        </init-param>
        doLoadConfig(config.getInitParameter("contextConfigLocation"));//
        //doLoadConfig(config.getInitParameter("contextConfigLocation"));//classpath:application.properties
        //2.根据配置文件扫描相关的类
        doScanner(p.getProperty("scanPackage"));
        //3. 初始化所有相关类的实例，并将其放入ioc容器，即map种
        doInstance();

        //4. 实现自动依赖注入
        doAutoWried();
        //5. 初始化handlerMapping
        initHanderMapping();

        // System.out.println("init()------初始化方法");
    }

    private void initHanderMapping() {
        if (ioc.isEmpty()) return;
        for (Map.Entry<String,Object> entry:ioc.entrySet()){
            Class<?> clazz=entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(DimController.class)) continue;
            String baseURL="";
            if (clazz.isAnnotationPresent(DimRequestMapping.class)){
                DimRequestMapping requestMapping=clazz.getAnnotation(DimRequestMapping.class);
                baseURL=requestMapping.value();
            }
//            Method[] methods=clazz.getMethods();
//            for (Method method:methods){
//                if (!method.isAnnotationPresent(DimRequestMapping.class)) continue;
//                DimRequestMapping requestMapping=method.getAnnotation(DimRequestMapping.class);
//                String url = (baseURL + requestMapping.value()).replaceAll("/+", "/");
//                handlerMapping.put(url,method);
//                System.out.println("mapping "+url+","+method);
//            }
            Method[] methods=clazz.getMethods();
            for (Method method:methods){
                if (!method.isAnnotationPresent(DimRequestMapping.class)) continue;
                DimRequestMapping requestMapping=method.getAnnotation(DimRequestMapping.class);
                String regex=("/"+baseURL+requestMapping.value().replaceAll("/+","/"));
                Pattern pattern=Pattern.compile(regex);
                handlerMapping.add(new  Handler(entry.getValue(), method, pattern));
                System.out.println("mapping "+regex+","+method);
            }

        }
    }

    private void doAutoWried() {
        if (ioc.isEmpty()) return;
        for (Map.Entry<String,Object>entry:ioc.entrySet()){
            //获得字段filed
            //不管室友，受保护的，都要进行注入
            Field[] fields=entry.getValue().getClass().getDeclaredFields();
            for (Field field:fields){
                if (!field.isAnnotationPresent(DimAutoWried.class)) continue;
                DimAutoWried dimAutoWried=field.getAnnotation(DimAutoWried.class);
                String beanName=dimAutoWried.value().trim();
                if ("".equals(beanName)){
                    beanName=field.getType().getName();
                }
                //强制授权，强行修改
                field.setAccessible(true);
                try {
                    field.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    private void doInstance() {
        if (classNames.isEmpty()){
            return;
        }else {
            //用反射机制将扫进来的所有classNaem初始化
            for(String className:classNames){
                Class<?>clazz= null;
                try {
                    clazz = Class.forName(className);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }

                //bean实例化，初始化ioc容器
                //不是所有牛奶都叫特伦舒
                //ioc规则
                //. ioc使用key/value
                //. 默认类名小写
                //. 如果自定义名字，要优先选择

                //1. key默认用首字母小写
                if(clazz.isAnnotationPresent(DimController.class)){
                    String beanName =lowerFirstCase(clazz.getSimpleName());
                    try {
                        ioc.put(beanName,clazz.newInstance());
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }else if (clazz.isAnnotationPresent(DimService.class)){
                    //2. 如果用户自定义名字，先选择用户子自定义的名字
                    DimService dimService=clazz.getAnnotation(DimService.class);
                    String beanName = dimService.value();
                    if (beanName.trim().equals("")){
                        beanName=lowerFirstCase(clazz.getSimpleName());
                    }
                    Object  instance= null;
                    try {
                        instance = clazz.newInstance();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    ioc.put(beanName,instance);

                    //3. 如果是接口，可巧妙用接口类型做key
                    Class<?>[] interfaces=clazz.getInterfaces();
                    for(Class<?>i:interfaces){
                        //将接口类型做key
                        ioc.put(i.getName(),instance);
                    }

                }else {
                    continue;
                }

            }
        }
    }

    private void doScanner(String packageName) {
        //进行递归扫描
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        File classDir=new File(url.getFile());

        for(File file:classDir.listFiles()){
            if (file.isDirectory()){
                doScanner(packageName+"."+file.getName());

            }else {
                classNames.add(packageName+"."+file.getName().replace(".class",""));
            }
        }
    }

    private void doLoadConfig(String location) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(location);
        try {
            p.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    //6.等待请求，进入运行阶段
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           /* String url=req.getRequestURI();
        String contextPath = req.getContextPath();
        url.replace(contextPath,"").replaceAll("/+","/");

        if (!handlerMapping.containsKey(url)) {
            resp.getWriter().write("404 not found");
            return;
        }
        Method m=handlerMapping.get(url);
        //拿到实例，拿到实参

        //反射调用
        1。 拿到方法method的实例instance
        2. 拿到实参，一般都是从request中取
       // m.invoke()
        System.out.println(m);*/


        try {
            doDispatch(req,resp);  //匹配对应的方法
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception,detail:\r\n"+ Arrays.toString( e.getStackTrace()));

        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        Handler handler=getHandler(req);
        if (handler==null){
            try {
                resp.getWriter().write("404 not found---251");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        Class<?>[] paramTypes=handler.method.getParameterTypes();
        //保存需要赋值的参数值
        Object[] paramValues=new Object[paramTypes.length];

        Map<String,String[]>params=req.getParameterMap();


        for (Map.Entry<String,String[]>param:params.entrySet()){
            String value= Arrays.toString(param.getValue()).replaceAll("\\[|\\]","").replaceAll(",\\s",",");
            if(!handler.paramIndexMapping.containsKey(param.getKey())) continue;
            int index=handler.paramIndexMapping.get(param.getKey());
            //将客户端传来的参数存入到paramValues里面
            paramValues[index]=convert(paramTypes[index],value);

        }
       // System.out.println(HttpServletRequest.class.getName()+"================269");
        int reqIndex=handler.paramIndexMapping.get(HttpServletRequest.class.getName());//这里报了一个空指针，应该是paramIndexMapping
        //里面没有HttpServletRequest.class.getName()这个键名
        paramValues[reqIndex]=req;
        int respIndex=handler.paramIndexMapping.get(HttpServletResponse.class.getName());
        paramValues[respIndex]=resp;
        try {
            handler.method.invoke(handler.controller,paramValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Handler getHandler(HttpServletRequest req) {
        if (handlerMapping.isEmpty()) return null;
        String url=req.getRequestURI();
        String contextPath=req.getContextPath();
        url=url.replace(contextPath,"").replaceAll("/+","/");
        for (Handler handler:handlerMapping){
            try {
                Matcher matcher=handler.pattern.matcher(url);
                //遍历匹配
                if (!matcher.matches()) continue;
                return handler;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private String lowerFirstCase(String str){
        char[] chars=str.toCharArray();
        chars[0] +=32;
        return String.valueOf(chars);
    }

    private Object convert(Class<?>type,String value){
        if(Integer.class==type){
            return Integer.valueOf(value);
        }
        return value;
    }

    /**
     * handler记录了controller种的requestmapping和method对应关系
     */
    private  class Handler{
        protected Object controller;  //保存方法对应的实例
        protected Method method;  //映射方法
        protected Pattern pattern;
        protected Map<String,Integer> paramIndexMapping;  //参数顺序

        public Handler(Object controller, Method method, Pattern pattern) {
            this.controller = controller;
            this.method = method;
            this.pattern = pattern;
            paramIndexMapping=new HashMap<String, Integer>();
            putParamIndexMapping(method);
        }

        /**
         * '提取方法加了注解的参数
         * @param method
         */
        private void putParamIndexMapping(Method method) {
                //提取注解参数
            Annotation[][] pa=method.getParameterAnnotations();
            for (int i=0;i<pa.length;i++){
                for (Annotation a:pa[i]){
                    if (a instanceof DimRequestParam){
                        String paramName=((DimRequestParam) a).value();
                        if (!"".equals(paramName.trim())){
                            System.out.println(paramName+"================343");
                            paramIndexMapping.put(paramName,i);
                        }
                    }
                }
            }

            //提取request和response
            Class<?>[] paramTypes=method.getParameterTypes();
            for (int i=0;i<paramTypes.length;i++){
                Class<?>type=paramTypes[i];
                System.out.println(type+"================356");
                if (type==HttpServletRequest.class||type==HttpServletResponse.class){
                    paramIndexMapping.put(type.getName(),i);
                }
            }
        }
    }

}
